package cn.big.bigd.biocode.controller;


import cn.big.bigd.biocode.dao.*;
import cn.big.bigd.biocode.dao.vocabulary.*;
import cn.big.bigd.biocode.dto.CategoryTree;
import cn.big.bigd.biocode.dto.Result;
import cn.big.bigd.biocode.dto.SearchDTO;
import cn.big.bigd.biocode.model.*;
import cn.big.bigd.biocode.model.vocabulary.*;
import cn.big.bigd.biocode.service.ToolService;
import cn.big.bigd.biocode.service.VisitTrackService;
import cn.big.bigd.biocode.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.FileSystemResource;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.transaction.Transactional;
import java.io.File;
import java.util.*;

/**
 *@Author wangfan
 *@Date 2018/1/18 16:26
 */

@Controller
@RequestMapping("tools")
@Scope("singleton")
public class ToolController {

    private static final Logger logger = LoggerFactory.getLogger(ToolController.class);

    @Value("${app.uploadPath}")
    private  String uploadPath;

    @Autowired
    CreditRoleDAO creditRoleDAO;
    @Autowired
    VisitTrackService visitTrackService;
    @Autowired
    ToolDAO toolDAO;
    @Autowired
    ToolService toolService;
    @Autowired
    ReleaseDAO releaseDAO;
    @Autowired
    TechDAO techDAO;

    @Autowired
    OrganizationDAO organizationDAO;

    @Autowired
    VisitTrackDAO visitTrackDAO;
    @Autowired

    DataTypeDAO dataTypeDAO;
    @Autowired
    PlatformDAO platformDAO;
    @Autowired
    TaskTypeDAO taskTypeDAO;
    @Autowired
    ToolTypeDAO toolTypeDAO;
    @Autowired
    UserInterfaceDAO userInterfaceDAO;
    @Autowired
    CategoryDAO categoryDAO;
    @Autowired
    RateDAO rateDAO;

    @RequestMapping(value = {"", "/"})
    public String home(ModelMap modelMap) {

        return "tool/index";
    }

    @RequestMapping(value = "search")
    public String search(Model model, SearchDTO s, Integer page, Integer size) {


        if (null == s.getSize()) {
            s.setSize(10);
        } else if (s.getSize() <= 5) {
            s.setSize(5);
        } else if (s.getSize() > 30) {
            s.setSize(30);
        }
        logger.info(s.toString());
        Page<Tool> toolPage = toolService.searchByCondition(s);
        logger.info("------" + toolPage.getTotalElements());
        model.addAttribute("tools", toolPage.getContent());
        model.addAttribute("totalPages", toolPage.getTotalPages());
        model.addAttribute("totalElements", toolPage.getTotalElements());
        model.addAttribute("s", s);
        setToolCommon(model);
        return "search/index";
    }

    @RequestMapping(value = {"edit"})
    public String edit(ModelMap modelMap, Integer id) {


        return "tool/edit";
    }

    @RequestMapping(value = "save",method = RequestMethod.POST)

    @ResponseBody
    public Result save(HttpServletRequest request, ModelMap modelMap, @RequestBody Tool tool) {
        HttpSession session = request.getSession();
        System.out.println(tool.toString());
        User user = (User) session.getAttribute("currentUser");
        tool.setUser(user);
        if (null != tool.getId()) {
            logger.info("" + tool.getId());
            tool.setCreatedAt(new Date());
            tool.setUpdatedAt(new Date());
            System.out.println(tool.getCredits());
            tool = toolService.save(tool);
            logger.info("" + tool.getId());

        } else {
            tool.setCreatedAt(new Date());
            tool.setUpdatedAt(new Date());
            tool = toolService.save(tool);
            logger.info("" + tool.getId());
        }


        return Result.ok(tool.getId());
    }

    //    @
    @RequestMapping(value = "new")
    public String show(Model model) {
        setToolCommon(model);
        return "tool/newOrEdit";
    }


    public void setToolCommon(Model model) {
        List<Tech> teches = techDAO.findAll();
        List<DataType> dataTypes = dataTypeDAO.findAll();
        List<Platform> platforms = platformDAO.findAll();
        List<UserInterface> userInterfaces = userInterfaceDAO.findAll();
        List<ToolType> toolTypes = toolTypeDAO.findAll();
        List<Category> categories = categoryDAO.findByParentId(null);
        List<CategoryTree> categoryTrees = new ArrayList<>();

        List<Country> countryList = CountryUtil.countryList();
        for (Category category : categories) {
            CategoryTree ct = new CategoryTree();
            ct.setRoot(category);
            ct.setLeaves(categoryDAO.findLevelsByCategory(category.getLft(), category.getRgt()));
            categoryTrees.add(ct);

        }
        model.addAttribute("countryList", countryList);
        model.addAttribute("teches", teches);
        model.addAttribute("platforms", platforms);
        model.addAttribute("userInterfaces", userInterfaces);
        model.addAttribute("toolTypes", toolTypes);
        model.addAttribute("dataTypes", dataTypes);
        model.addAttribute("categoryTrees", categoryTrees);
    }

    @RequestMapping(value = "{id}/show")
    public String show(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("id is not correct");
        }
        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);

        List<Integer> userIds = rateDAO .calUserIdsByTools(id);
        model.addAttribute("userIds", userIds);


        return "tool/show";
    }

    @RequestMapping(value = "{id}/manual")
    public String manual(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);

        return "tool/manual";
    }

    @RequestMapping(value = "{id}/stat")
    public String stat(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        String sql ="SELECT releases.version, YEAR(date), MONTH(date), sum(visit_tracks.count) FROM visit_tracks\n" +
                "  INNER JOIN releases ON visit_tracks.target_id=releases.id\n" +
                "  INNER JOIN tools ON tools.id=releases.tool_id\n" +
                "WHERE tools.id = "+id+" AND visit_tracks.target_type='release_download'\n" +
                "GROUP BY visit_tracks.target_id, YEAR(date), MONTH(date)";

       List<Map<String,Object>> statByMonths= toolService.findMapListBySql(sql);

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);

        return "tool/stat";
    }
    @RequestMapping(value = "{id}/manual/update",method = RequestMethod.POST)
    public String manualUpdate(Model model, @PathVariable("id") Integer id, String manual) {
        Tool t = toolDAO.findOne(id);
        t.setManual(manual);
        toolService.save(t);
        return "redirect:/tools/" + t.getId() + "/manual";
    }

    @RequestMapping(value = "{id}/manual/edit")
    public String manualEdit(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);

        return "tool/manual_edit";
    }

    @RequestMapping(value = "{id}/releases")
    public String releases(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("id is not correct");
        }
        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);
        if(releaseDAO.countByTool(t)>0){
            List<Release> releases = releaseDAO.findByToolOrderByCreatedAtDesc(t);

            String version =releases.get(0).getVersion();
            return "redirect:/tools/" + t.getId() + "/releases/"+version;


        }else{


            return "tool/releases";
        }




    }
    @RequestMapping(value = "{id}/releases/{version:.+}")
    public String releases(Model model, @PathVariable("id") Integer id ,@PathVariable("version") String version) {
        logger.info(id+"");
        logger.info(version);
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        Release release = releaseDAO.findByToolAndVersion(t,version);
        if(null==release){
            throw new RuntimeException("version  not correct");
        }
        List<Release> releases = releaseDAO.findByToolOrderByCreatedAtDesc(t);



        String dir =uploadPath+id+"/"+release.getId();
        List<String> fileNames =new ArrayList<>();
        File file =new File(dir);
        if(file.isDirectory()){
            File[] files =  file.listFiles();
            for (File f : files) {
                if(!f.getName().equals(".DS_Store")){
                    fileNames.add(f.getName());
                }

            }
        }

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);
        model.addAttribute("release", release);
        model.addAttribute("fileNames", fileNames);
        model.addAttribute("version", version);
        model.addAttribute("releases", releases);
        return "tool/releases";
    }
    @RequestMapping(value = "{id}/releases/{version:.+}/edit")
    public String releaseEdit(Model model, @PathVariable("id") Integer id ,@PathVariable("version") String version) {
        logger.info(id+"");
        logger.info(version);
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        Release release = releaseDAO.findByToolAndVersion(t,version);
        if(null==release){
            throw new RuntimeException("version  not correct");
        }

        String dir =uploadPath+id+"/"+release.getId();
        List<Map> files =new ArrayList<>();
        File file =new File(dir);
        if(file.isDirectory()){
            File[] fs =  file.listFiles();
            for (File f : fs) {
                if(!f.getName().equals(".DS_Store")) {
                    Map m = new HashMap();
                    m.put("fileName", f.getName());
                    m.put("fileLength", f.length());
                    files.add(m);
                }
            }
        }

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("tool", t);
        model.addAttribute("release", release);
        model.addAttribute("files", files);
        model.addAttribute("version", version);
        return "tool/releases_newOrEdit";
    }

    @RequestMapping(value = "{id}/releases/new")
    public String releasesNew(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        model.addAttribute("id", id);
        model.addAttribute("tool", t);
        return "tool/releases_newOrEdit";
    }
    @RequestMapping(value = "{id}/releases/{version:.+}/file/download")
    @ResponseBody
    public ResponseEntity<FileSystemResource> download(HttpServletRequest request, @PathVariable("id") Integer id, @PathVariable("version") String version, String filename) {
        logger.info(id+"");
        logger.info(filename);
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        Release release =releaseDAO.findByToolAndVersion(t,version);
        if(null==release){
            throw new RuntimeException("release version  not exist");
        }
        String filepath =uploadPath+""+id+"/"+release.getId()+"/"+filename;
        logger.info(filepath);
        File  f =new File(filepath);
        if(f.isFile()){


           String ip= IpUtils.getIpFromRequest(request);
            logger.info(ip);
            String code ="UNKNOWN";
            if(IpUtils.isIPv4Private(ip)){
                code ="CN";
            }
    
            GeoLocation geoLocation =GeoIPv4.getLocation(ip);
            if(null!=geoLocation){
                 code =geoLocation.getCountryCode();
            }

            visitTrackService.increment(code,"release_download",release.getId());
            return DownloadUtil.download(f);
        }
        return null;

    }
    @RequestMapping(value = "{id}/releases/{version:.+}/file/delete")
    public String  fileDelete( @PathVariable("id") Integer id, @PathVariable("version") String version, String filename) {
        logger.info(id+"");
        logger.info(filename);
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        Release release =releaseDAO.findByToolAndVersion(t,version);
        if(null==release){
            throw new RuntimeException("release version  not exist");
        }
        String filepath =uploadPath+""+id+"/"+release.getId()+"/"+filename;
        logger.info(filepath);
        File  f =new File(filepath);
        if(f.isFile()){
           f.delete();
        }
        return "redirect:/tools/" + t.getId() + "/releases/"+release.getVersion()+"/edit";

    }

    @RequestMapping(value = "{id}/releases/save")
    public String releasesSave(Model model, @PathVariable("id") Integer id,Release release,MultipartFile file) throws Exception {
        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(null==t){
            throw new RuntimeException("toolId  not correct");
        }
        release.setTool(t);

        if(null!=releaseDAO.findByToolAndVersion(t,release.getVersion())){
            Release r =releaseDAO.findByToolAndVersion(t,release.getVersion());
            release.setId(r.getId());
            release.setCreatedAt(r.getCreatedAt());


        }else {
            release.setCreatedAt(new Date());
        }
        release.setUpdatedAt(new Date());

        if(!file.isEmpty()){
            String filepath =uploadPath+""+id+"/"+release.getId()+"/"+file.getOriginalFilename();
            File f = new File(filepath);
            f.getParentFile().mkdirs();
            file.transferTo(f);

        }
        releaseDAO.save(release);

        return "redirect:/tools/" + t.getId() + "/releases/"+release.getVersion()+"/edit";
    }

  /*  @RequestMapping(value = "{id}/new/releases")
    public String newReleases(Model model, @PathVariable("id") Integer id) {
        logger.info(id+"");
        model.addAttribute("id", id);
        return "tool/releases_newOrEdit";
    }*/
    @RequestMapping(value = "{id}/edit")
    @Transactional
    public String edit(Model model,HttpServletRequest request, @PathVariable("id") Integer id) throws Exception {
        logger.info(id+"");
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("currentUser");

        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(t==null){
            throw  new Exception("id not correct");
        }
        if(null==user){
            throw  new Exception("no auth");

        }
        if(t.getUser().getId()!=user.getId()){
            throw  new Exception("no auth");
        }

        model.addAttribute("accession", StringUtil.generateAccession(id));
        model.addAttribute("roles", creditRoleDAO.findAll());
        model.addAttribute("tool", t);
        setToolCommon(model);
        return "tool/newOrEdit";
    }


    @RequestMapping(value = "{id}/delete")
    public String delete(Model model,HttpServletRequest request, @PathVariable("id") Integer id) throws Exception {
        logger.info(id+"");
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("currentUser");

        logger.info(id+"");
        Tool t = toolDAO.findOne(id);
        if(t==null){
            throw  new Exception("id not correct");
        }
        if(null==user){
            throw  new Exception("no auth");

        }
        if(t.getUser().getId()!=user.getId()){
            throw  new Exception("no auth");
        }

        toolService.deleteOne(id);
        return "redirect:/index";
    }

}
